package cn.dengta.webapp.trade.service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Resource;

import cn.dengta.common.model.Status;
import cn.dengta.common.web.Page;
import cn.dengta.context.model.*;
import cn.dengta.context.web.RequestContextImpl;
import cn.dengta.webapp.base.service.BaseServiceImpl;
import cn.dengta.webapp.trade.dao.MetaConfigDao;
import cn.dengta.webapp.trade.entity.meta.*;
import cn.dengta.webapp.trade.model.MetaConfigType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import me.codeplayer.util.*;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import static cn.dengta.webapp.trade.entity.meta.MetaConfig.*;

/**
 * 系统前端资料配置表 服务实现类
 *
 * @author lgy
 * @since 2022-03-26
 */
@Service
public class MetaConfigService extends BaseServiceImpl<MetaConfig, MetaConfigDao, Long> {

	@Resource
	StringRedisTemplate stringRedisTemplate;

	/**
	 * 缓存（一天）
	 */
	protected List<MetaConfig> typeLoader(boolean flush, List<String> types) {
		HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();
		final String key = RedisKey.META_CONFIGS;
		if (flush || Boolean.FALSE.equals(stringRedisTemplate.hasKey(key))) {
			QueryWrapper<MetaConfig> wrapper = new QueryWrapper<MetaConfig>().ge(STATUS, Status.YES.value);
			wrapper.notIn(TYPE, MetaConfigType.NICKNAME.getValue());
			List<MetaConfig> configs = selectList(wrapper);
			if (X.isValid(configs)) {
				Map<String, String> cfgMap = new HashMap<>();
				for (Map.Entry<String, List<MetaConfig>> entry : configs.stream().collect(Collectors.groupingBy(c -> c.getType().getValue().toString())).entrySet()) {
					cfgMap.put(entry.getKey(), JSONUtil.encode(entry.getValue()));
				}
				opsForHash.putAll(key, cfgMap);
				stringRedisTemplate.expire(key, EasyDate.MILLIS_OF_DAY, TimeUnit.MILLISECONDS);
			}
		}
		if (!X.isValid(types)) {
			return Collections.emptyList();
		}
		List<String> entries = opsForHash.multiGet(key, types);
		List<MetaConfig> cfgList = null;
		if (X.isValid(entries)) {
			cfgList = new ArrayList<>();
			for (String str : entries) {
				if (StringUtil.notEmpty(str)) {
					cfgList.addAll(JSONUtil.parseArray(str, MetaConfig.class));
				}
			}
		}
		return X.isValid(cfgList) ? cfgList : Collections.emptyList();
	}

	public List<MetaConfig> findCacheByType(boolean flush, Language language, MetaConfigType type, boolean defaultCfg) {
		List<MetaConfig> configs = findCacheByType(flush, language, type, null);
		if (!X.isValid(configs) && defaultCfg) {
			configs = findCacheByType(flush, Language.DEFAULT, type, null);
			if (!X.isValid(configs) && !flush) {
				configs = findCacheByType(true, Language.DEFAULT, type, null);
			}
		}
		return configs;
	}

	public <T extends MetaRuleConfig> T checkConfig(boolean flush, Language language, MetaConfigType type, boolean defaultCfg) {
		List<MetaConfig> configs = findCacheByType(flush, language, type, defaultCfg);
		if (X.isValid(configs)) {
			return X.map(configs.get(0), MetaConfig::getResolvedCfg);
		}
		return null;
	}

	public List<MetaConfig> findCacheByType(Language language, MetaConfigType type) {
		return findCacheByType(false, language, type, null);
	}

	public List<MetaConfig> findCacheByType(boolean repetition, boolean flush, Language language, MetaConfigType type, String tag) {
		if (type == null) {
			return Collections.emptyList();
		}
		List<MetaConfig> types = typeLoader(flush, List.of(type.getValue().toString()));
		List<MetaConfig> cfgList = new ArrayList<>();
		if (X.isValid(types)) {
			boolean repeatable = type.repeatable;
			for (MetaConfig cfg : types) {
				if (language != null && cfg.getLanguage() != language) {
					continue;
				}
				if (StringUtil.notEmpty(tag) && !Objects.equals(cfg.getTag(), tag)) {
					continue;
				}
				cfgList.add(cfg);
				if (!repetition && !repeatable) {
					return cfgList;
				}
			}
		}
		return cfgList;
	}

	public List<MetaConfig> findCacheByType(boolean flush, Language language, MetaConfigType type, String tag) {
		return findCacheByType(false, flush, language, type, tag);
	}


	public List<MetaConfig> random(MetaConfigType type, int size) {
		if (type == null || size < 1) {
			return Collections.emptyList();
		}
		final Language language = RequestContextImpl.get().sessionLanguage();
		type = language.forChina() ? MetaConfigType.SLOGAN_CN : type;
		final List<MetaConfig> types = typeLoader(false, List.of(type.getValue().toString()));
		List<MetaConfig> cfgList = null;
		if (X.isValid(types)) {
			int len = X.size(types);
			int fromIndex = len > size ? RandomUtil.getInt(0, len - size) : 0;
			cfgList = types.subList(fromIndex, Math.min(fromIndex + size, len));
		}
		return X.isValid(cfgList) ? cfgList : Collections.emptyList();
	}

	/**
	 * 根据查询条件返回任务规则列表
	 */
	public Page<MetaConfig> findPage(Page<MetaConfig> page, MetaConfig input) {
		return baseDao.selectPage(page, popWrapper(X.map(input, MetaConfig::getName), X.map(input, MetaConfig::getType), X.map(input, MetaConfig::getLanguage), null));
	}

	public MetaConfig getRandomNickname(Language language, MetaConfigType ruleType) {
		return baseDao.getRandomNickname(language, ruleType);
	}

	public QueryWrapper<MetaConfig> popWrapper(String name, MetaConfigType type, Language language, String tag) {
		return new QueryWrapper<MetaConfig>()
				.like(StringUtil.notEmpty(name), NAME, name)
				.eq(type != null, TYPE, type)
				.eq(StringUtil.notEmpty(tag), TAG, tag)
				.eq(language != null, LANGUAGE, language);
	}

	/**
	 * 根据筛选条件获取数据
	 */
	public List<MetaConfig> find(String name, MetaConfigType type, Language language, String tag) {
		QueryWrapper<MetaConfig> wrapper = popWrapper(name, type, language, tag)
				.ge(STATUS, Status.YES.value);
		return selectList(wrapper);
	}

	/**
	 * 返回指定任务类型的规则
	 */
	public MetaConfig getByType(Language language, MetaConfigType type, String tag) {
		QueryWrapper<MetaConfig> wrapper = popWrapper(null, type, language, tag)
				.ge(STATUS, Status.YES.value);
		return baseDao.selectOne(wrapper);
	}

	public MetaConfig getByType(Language language, MetaConfigType type) {
		return getByType(language, type, null);
	}

	public List<MetaConfig> queryLoaderForType(boolean flush, Language language, MetaConfigType type) {
		return findCacheByType(flush, language, type, null);
	}

	public List<MetaConfig> queryLoaderListForType(MetaConfigType... queryTypes) {
		if (queryTypes == null || queryTypes.length == 0) {
			return Collections.emptyList();
		}
		List<String> types = Arrays.stream(queryTypes).map(t -> String.valueOf(t.getValue())).collect(Collectors.toList());
		return typeLoader(false, types);
	}

	public PullWiresRuleV1 getPullWireRuleByWesKit(WesKit wesKit, MetaConfigType type) {
		QueryWrapper<MetaConfig> wrapper = popWrapper(null, type, null, wesKit.name())
				.ge(STATUS, Status.YES.value);
		MetaConfig metaConfig = baseDao.selectOne(wrapper);
		return X.map(metaConfig, MetaConfig::getResolvedCfg);
	}

}
